В конце 60-х годов прошлого века в США было отмечено явление под названием «software crisis» (кризис ПО). Это выражалось в том, что большие проекты стали выполняться с отставанием от графика или с превышением сметы расходов, разработанный продукт не обладал требуемыми функциональными возможностями, производительность его была низка, качество получаемого программного обеспечения не устраивало потребителей [1] .
Кризис программного обеспечения привел к необходимости создания нового способа создания программ, который снижал бы общие затраты на протяжении всего жизненного цикла программы, — от замысла до завершения эксплуатации (морального старения). Такая технология появилась в начале 70-х годов и была названа структурным программированием. В основе структурного программирования лежит сочетание теории программирования и личного опыта высококвалифицированных программистов, а также учет современных требований к программам и промышленного характера их производства.
Главное требование, которому должна удовлетворять программа, — работать в полном соответствии со спецификацией и адекватно реагировать на любые действия пользователя. Кроме этого, программа должна быть выпущена точно к заявленному сроку, и допускать оперативное внесение необходимых изменений и дополнений. Иными словами, современные критерии качества программы — это, прежде всего, надежность, а также возможность точно планировать производство программы и ее сопровождение. Для достижения этих целей программа должна иметь простую структуру, быть хорошо читаемой и легко модифицируемой [2].
Структурное программирование — это технология создания программ, позволяющая путем соблюдения определенных правил уменьшить время разработки и количество ошибок, а также облегчить возможность модификации программы.
Структурный подход охватывает все стадии разработки проекта: анализ требований, проектирование, собственно программирование и тестирование.
Структурный подход к программированию позволил успешно создавать достаточно крупные проекты, но сложность программного обеспечения продолжала возрастать, и требовались все более развитые средства ее преодоления. Идеи структурного программирования получили свое дальнейшее развитие в объектно-ориентированном программировании (ООП) — технологии, позволяющей достичь простоты структуры и управляемости очень крупных программных систем.
Одной из основных проблем, которые приходится решать при создании больших и сложных систем любой природы, в том числе и ПО, является проблема сложности. Ни один разработчик не в состоянии выйти за пределы человеческих возможностей и понять всю систему в целом. Единственный эффективный подход к решению этой проблемы, который выработало человечество за всю свою историю, заключается в построении сложной системы из небольшого количества крупных частей, каждая из которых, в свою очередь, строится из частей меньшего размера, и т.д., до тех пор, пока самые небольшие части можно будет строить из имеющегося материала. Этот подход известен под самыми разными названиями, среди них такие, как «разделяй и властвуй» (divide et imperd), иерархическая декомпозиция и др. По отношению к проектированию сложной программной системы это означает, что ее необходимо разделить (декомпозировать) на небольшие подсистемы, каждую из которых можно разрабатывать независимо от других. Это позволяет при разработке подсистемы любого уровня иметь дело только с ней, а не со всеми остальными частями системы [1].
Структурные методы являются строгой дисциплиной системного анализа и проектирования. Методы структурного анализа и проектирования стремятся преодолеть сложность больших систем путем расчленения их на части («черные ящики») и иерархической организации этих «черных ящиков». Выгода в использовании «черных ящиков» заключается в том, что их пользователю не требуется знать, как они работают, необходимо знать лишь их входы и выходы, а также назначение (т.е. функции, которые они выполняет).
Таким образом, первым шагом упрощения сложной системы является ее разбиение на «черные ящики», при этом такое разбиение должно удовлетворять следующим критериям:
• каждый «черный ящик» должен реализовывать единственную функцию системы;
• функция каждого «черного ящика» должна быть легко понимаема независимо от сложности ее реализации;
• связь между «черными ящиками» должна вводиться только при наличии связи между соответствующими функциями системы;
• связи между «черными ящиками» должны быть простыми, насколько это возможно, для обеспечения независимости между ними.
Второй важной идеей, лежащей в основе структурных методов, является идея иерархии. Для понимания сложной системы недостаточно разбиения ее на части, необходимо эти части организовать определенным образом, а именно в виде иерархических структур. Все сложные системы Вселенной организованы в иерархии: от галактик до элементарных частиц. Человек при создании сложных систем также подражает природе. Любая организация имеет директора, заместителей по направлениям, иерархию руководителей подразделений, рядовых служащих [1].
Кроме того, структурные методы широко используют визуальное моделирование, служащее для облегчения понимания сложных систем.
Структурным анализом принято называть метод исследования системы, начинающий с ее общего обзора, который затем детализируется, приобретая иерархическую структуру со все большим числом уровней [1].
Для таких методов характерно:
• разбиение системы на уровни абстракции с ограничением числа элементов на каждом из уровней (обычно от 3 до 6-7);
• ограниченный контекст, включающий лишь существенные на каждом уровне детали;
• использование строгих формальных правил записи;
• последовательное приближение к конечному результату.
В структурном анализе основным методом разбиения на уровни абстракции является функциональная декомпозиция, заключающаяся в декомпозиции (разбиении) системы на функциональные подсистемы, которые, в свою очередь, делятся на подфункции, т.е. - на задачи и так далее до конкретных процедур. При этом система сохраняет целостное представление, в котором все составляющие компоненты взаимоувязаны. При разработке системы «снизу вверх» от отдельных задач ко всей системе целостность теряется, возникают проблемы при описании информационного взаимодействия отдельных компонентов.
Все наиболее распространенные методы структурного подхода базируются на ряде общих принципов. Базовыми принципами являются:
• принцип «разделяй и властвуй» — принцип решения трудных проблем путем разбиения их на множество меньших независимых задач, легких для понимания и решения;
• принцип иерархического упорядочения — принцип организации составных частей системы в иерархические древовидные структуры с добавлением новых деталей на каждом уровне.
Выделение двух базовых принципов не означает, что остальные принципы являются второстепенными, поскольку игнорирование любого из них может привести к нежелательным последствиям (вплоть до неудачного завершения проекта). Основными из этих принципов являются:
• принцип абстрагирования — выделение существенных аспектов системы и отвлечение от несущественных;
• принцип непротиворечивости — обоснованность и согласованность элементов системы;
• принцип структурирования данных — данные должны быть структурированы и иерархически организованы.
Прикладные программы чаще всего слишком сложны, чтобы быть написанными как единое целое или разработанными одним программистом. Поэтому сложность программ долгое время вызывала трудности при проектировании систем и программного обеспечения. Программы стали слишком велики, чтобы их можно было представить во всех подробностях как единое целое. Поэтому большие программы на этапе проектирования стали разбивать на логические (функциональные) модули.
Под модульным программированием (модуляризацией) понимается разделение программы на части по некоторым установленным правилам.
Эти части на этапе кодирования могут быть реализованы в виде модулей или подпрограмм в зависимости от их размера и выбранного языка программирования.
Для того чтобы логический модуль можно было закодировать, необходимо предварительно описать его поведение, т.е. специфицировать. Для спецификации функций логических модулей с точки зрения входных выходных данных и связи между ними используют HIPO-диаграмму. Её название образовано из первых букв английского словосочетания hierarchy-input-processing-output (иерархическое описание вход-обработка-выход) [3].
Программы разбиваются на модули для того, чтобы:/p>
• упростить их разработку и реализацию;
• облегчить чтение программ;
• упростить их настройку и модификацию;
• облегчить работу с данными, имеющими сложную структуру;
• избежать чрезмерной детализации алгоритмов;
• обеспечить более выгодное размещение программ в памяти ЭВМ.
Правильная декомпозиция является главным способом преодоления сложности разработки больших систем ПО. Понятие «правильная» по отношению к декомпозиции означает следующее:
• количество связей между отдельными модулями должно быть минимальным (принцип «слабой связанности» - Low Coupling);
• связность отдельных частей внутри каждого модуля должна быть максимальной (принцип «сильного сцепления» — High Cohesion).
Связность модуля определяется как мера зависимости его частей.
Чем выше связность модуля, тем лучше результат проектирования. Для обозначения связности используется также понятие силы связности модуля.
Сцепление модулей представляет собой меру относительной независимости модулей, которая определяет их читабельность и сохранность.
Независимые модули могут быть модифицированы без переделки каких-либо других модулей. Слабое сцепление более желательно, так как это означает высокий уровень их независимости. Модули являются полностью независимыми, если каждый из них не содержит о другом никакой информации. Чем больше информации о других модулях используется в них, тем менее они независимы и тем теснее сцеплены.
Методы проектирования программ, основанные на модульном принципе, делятся на три группы:
• методы нисходящего проектирования,
• методы расширения ядра и
• методы восходящего проектирования.
На практике обычно применяются различные сочетания этих методов.
Рассмотрим применение функциональной декомпозиции на следующем примере.
Пусть нам необходимо разработать консольное приложение для перевода числа представленного строкой в системе счисления с основанием р (основание может меняться в диапазоне от 2 до 16) в вещественное значение.
Проектирование программы начинаем с функциональной декомпозиции задачи и построения на её основе схемы иерархии логических модулей. Схема иерархии логических модулей – это результат отображения разбиения исходной задачи на части на уровне проекта. Каждый логический модуль осуществляет преобразование некоторых входных данных в определённый результат и может быть снова разделён на части. Схема иерархии логических модулей отображает связь модулей по управлению, т.е. на ней с помощью прямых линий показано, какие модули могут быть вызваны из данного модуля. Результатом разбиения нашей задачи на части может быть схема иерархии, представленная ниже.

Рисунок 1 - Схема иерархии логических модулей.
1. HIPO - диаграмма
HIPO - диаграмма предназначена для спецификации (описания) требований к программе или функциональному (логическому) модулю.
Она является результатом анализа процесса обработки данных, выполняемого программой, с точки зрения исходных данных и результатов работы программы.
HIPO - диаграмма имеет следующий вид:

В разделе “вход” перечисляются имена входных данных, их типы, диапазоны возможных значений.
В разделе “выход” перечисляются имена выходных данных, их типы, диапазоны возможных значений.
В разделе “обработка” для каждого выхода необходимо указать, с какими входами он связана и как. В этом разделе содержится описание того, что делает программа, а не как она это делает.
Таким образом, HIPO-диаграмма – это описание поведения процесса обработки данных в таких существенных признаках, как входные значения, выходные значения и связь между ними.
Рассмотрим построение HIPO-диаграммы для модуля «1.1. Перевод р-ичного целого в число» с Рисунок 1.

2. Метод нисходящего проектирования
Метод нисходящего проектирования подобен методу получения детального изображения из более общего вида с помощью телескопического увеличения. На, начальном шаге формируется предложение, описывающее функцию всей программы. Затем определяются ее подфункции. Эта процедура является рекурсивной, т. е., следуя ей, каждая из подфункций может расчленяться до тех пор, пока ее составные части не будут окончательно уточнены. Метод нисходящего проектирования, иногда называемый функциональной декомпозицией, основан на двух стратегиях: пошаговом уточнении, разработанном Е. Дейкстрой, и анализе сообщений, базирующемся на работах Йодана, Константайна и Мейерса. Эти стратегии отличаются способами определения начальных спецификаций, методами, используемыми при разбиении задачи на части, и правилами записи.
3. Метод расширения ядрапроектирования
Метод расширения ядра отличается от способа нисходящего проектирования: в нем больше внимания вначале уделяется выявлению множества вспомогательных функций, а не определению функции всей программы в целом. Эти функции можно получить, применяя методы проектирования структур данных, которые используются при иерархическом модульном проектировании, разработанном Джексоном, или определяя области хранения данных с последующим анализом связанных с ними функциональных единиц (как в методе определения спецификаций модуля, разработанном Парнасом)
4. Метод восходящего проектирования
При использовании метода восходящего проектирования в первую очередь определяются вспомогательные функции, которые могут потребоваться для проектируемой программы. Модульная декомпозиция заключается в нахождении ключевых модулей промежуточных уровней, которые затем разрабатываются восходящим и нисходящим способами одновременно. Эти модули не являются вспомогательными в том смысле, что потребность в них возникает в нескольких точках программы. Необходимость в использовании этих модулей может возникать в других программах или системах.
Функции, определяемые как вспомогательные при восходящем проектировании, реализуются с помощью модулей самых нижних уровней.
В теории программирования доказано, что программу для решения задачи любой сложности можно составить только из трех структур, называемых следованием, ветвлением и циклом. Этот результат установлен Боймом и Якопини еще в 1966 году путем доказательства того, что любую программу можно преобразовать в эквивалентную, состоящую только из этих структур и их комбинаций.
Следование, ветвление и цикл называют базовыми конструкциями структурного программирования. Следованием называется конструкция, представляющая собой последовательное выполнение двух или более операторов (простых или составных). Ветвление задает выполнение либо одного, либо другого оператора в зависимости от выполнения какого-либо условия. Цикл задает многократное выполнение оператора (рисунок 2). Особенностью базовых конструкций является то, что любая из них имеет только один вход и один выход, поэтому конструкции могут вкладываться друг в друга произвольным образом, например, цикл может содержать следование из двух ветвлений, каждое из которых включает вложенные циклы (рисунок 3).

Рисунок 2 - Базовые конструкции структурного программирования

Рисунок 3 - Вложение базовых конструкций
Целью использования базовых конструкций является получение программы простой структуры. Такую программу легко читать (а программы чаще приходится читать, чем писать), отлаживать и при необходимости вносить в нее изменения. Структурное программирование часто называли «программированием без goto», и в этом есть большая доля правды: частое использование операторов передачи управления в произвольные точки программы затрудняет прослеживание логики ее работы. С другой стороны, никакие принципы нельзя возводить в абсолют, и есть ситуации, в которых использование goto оправдано и приводит, напротив, к упрощению структуры программы.
В большинстве языков высокого уровня существует несколько реализаций базовых конструкций. Они введены для удобства программирования, и в каждом случае надо выбирать наиболее подходящие средства. Главное, о чем нужно помнить даже при написании самых простых программ, — что они должны состоять из четкой последовательности блоков строго определенной конфигурации.
Структурный подход к программированию, как уже упоминалось, охватывает все стадии разработки проекта: анализ требований, проектирование, собственно программирование (кодирование) и тестирование. Задачи, которые при этом ставятся, — уменьшение числа возможных ошибок за счет применения только допустимых структур, возможно более раннее обнаружение ошибок и упрощение процесса их исправления. Ключевыми идеями структурного подхода являются
• нисходящая разработка,
• структурное программирование и
• нисходящее тестирование.
Приведенные ниже этапы создания программ рассчитаны на достаточно большие проекты, разрабатываемые коллективом программистов. Для программы небольшого объема каждый этап упрощается, но содержание и последовательность этапов не изменяются.
I этап. Создание любой программы начинается с анализа требований пользователей - постановки задачи. Изначально задача ставится в терминах предметной области, и необходимо перевести ее в термины, более близкие к программированию. Поскольку программист редко досконально разбирается в предметной области, а заказчик — в программировании (простой пример: требуется написать бухгалтерскую программу), постановка задачи может стать весьма непростым итерационным процессом. Кроме того, при постановке задачи заказчик зачастую не может четко и полно сформулировать свои требования и критерии.
Постановка задачи завершается созданием технического задания, а затем внешней спецификации программы, включающей в себя:
• описание исходных данных и результатов (типы, форматы, точность, способ передачи, ограничения);
• описание задачи, реализуемой программой;
• способ обращения к программе;
• описание возможных аварийных ситуаций и ошибок пользователя.
Таким образом, программа рассматривается как черный ящик, для которого определена функция и входные и выходные данные.
II этап. Разработка внутренних структур данных. Большинство алгоритмов зависит от того, каким образом организованы данные, поэтому интуитивно ясно, что начинать проектирование программы надо не с алгоритмов, а с разработки структур, необходимых для представления входных, выходных и промежуточных данных. При этом принимаются во внимание многие факторы, например, ограничения на размер данных, необходимая точность, требования к быстродействию программы. Структуры данных могут быть статическими или динамическими.
III этап. Проектирование (определение общей структуры и взаимодействия модулей). На этом этапе применяется технология нисходящего проектирования программы, основная идея которого теоретически проста: разбиение задачи на подзадачи меньшей сложности - функциональные (логические) модули, которые можно рассматривать раздельно. Главный критерий разбиения (функциональной декомпозиции) — минимизация взаимодействия модулей. При этом используется метод пошаговой детализации. Можно представить себе этот процесс так, что сначала программа пишется на языке некоторой гипотетической машины, которая способна понимать самые обобщенные действия, а затем каждое из них описывается на более низком уровне абстракции, и так далее. Очень важной на этом этапе является спецификация интерфейсов, то есть способов взаимодействия подзадач.
Для каждой подзадачи составляется внешняя спецификация, аналогичная приведенной выше. Для каждого подзадачи (модуля) описывается его функция, например, с помощью HIPO-диаграммы. Одна задача может реализовываться с помощью нескольких модулей и, наоборот, в одном модуле может решаться несколько задач. На более низкий уровень проектирования переходят только после окончания проектирования верхнего уровня. Для каждого модуля разрабатывается алгоритм обработки данных с помощью базовых конструкций структурного программирования и записывается в обобщенной форме — например, словесной, в виде обобщенных блок-схем или другими способами.
На этапе проектирования следует учитывать возможность будущих модификаций программы и стремиться проектировать программу таким образом, чтобы вносить изменения было возможно проще.
Процесс проектирования является итерационным, поскольку в программах реального размера невозможно продумать все детали с первого раза.
IV этап. Структурное программирование. Процесс программирования также организуется по принципу «сверху вниз»: вначале кодируются модули самого верхнего уровня и составляются тестовые примеры для их отладки, при этом на месте еще не написанных модулей следующего уровня ставятся «заглушки» — временные программы. «Заглушки» в простейшем случае просто выдают сообщение о том, что им передано управление, а затем возвращают его в вызывающий модуль. В других случаях «заглушка» может выдавать значения, заданные заранее или вычисленные по упрощенному алгоритму.
Таким образом, сначала создается логический скелет программы, который затем обрастает плотью кода.
Можно применять к процессу программирования восходящую технологию — написать и отладить сначала модули нижнего уровня, а затем объединять их в более крупные фрагменты, но этот подход имеет ряд недостатков.
Во-первых, в процессе кодирования верхнего уровня могут быть вскрыты те или иные трудности проектирования более низких уровней программы (просто потому, что при написании программы ее логика продумывается более тщательно, чем при проектировании). Если подобная ошибка обнаруживается в последнюю очередь, требуются дополнительные затраты на переделку уже готовых модулей нижнего уровня.
Во-вторых, для отладки каждого модуля, а затем более крупных фрагментов программы требуется каждый раз составлять свои тестовые примеры, и программист часто вынужден имитировать то окружение, в котором должен работать модуль. Нисходящая же технология программирования обеспечивает естественный порядок создания тестов — возможность нисходящей отладки, которая рассмотрена далее.
Этапы проектирования и программирования совмещены во времени: в идеале сначала проектируется и кодируется верхний уровень, затем — следующий, и так далее. Такая стратегия применяется потому, что в процессе кодирования может возникнуть необходимость внести изменения, отражающиеся на модулях нижнего уровня.
V этап. Нисходящее тестирование. Этот этап записан последним, но это не значит, что тестирование не должно проводиться на предыдущих этапах. Проектирование и программирование обязательно должны сопровождаться написанием набора тестов — проверочных исходных данных и соответствующих им наборов ожидаемых результатов.
Необходимо различать процессы тестирования и отладки программы.
Тестирование — это процесс выполнения программ с целью обнаружения ошибок.
Ошибкой считается любое не соответствие работы программы заданной на неё спецификации.
Хорошим считается тест, который имеет высокую вероятность обнаружения еще не выявленной ошибки. Удачным считается тест, который обнаруживает еще не выявленную ошибку.
Отладка программы - это процесс, осуществляемый после удачного выполнения теста. Процесс отладки начинается при обнаружении ошибки (например, при удачном завершении теста) и проводится в два этапа:
• определяется природа и местонахождение подозреваемой ошибки в программе;
• фиксируется или исправляется ошибка.
Отладка — процесс исправления ошибок в программе, при этом цель исправить все ошибки не ставится. Исправляют ошибки, обнаруженные при тестировании. При планировании следует учитывать, что процесс обнаружения ошибок подчиняется закону насыщения, то есть большинство ошибок обнаруживается на ранних стадиях тестирования, и чем меньше в программе осталось ошибок, тем дольше искать каждую из них.
Для исчерпывающего тестирования программы необходимо проверить каждую из ветвей алгоритма. Общее число ветвей определяется комбинацией всех альтернатив на каждом этапе. Это конечное число, но оно может быть очень большим, поэтому программа разбивается на фрагменты, после исчерпывающего тестирования, которых они рассматриваются как элементарные узлы более длинных ветвей. Кроме данных, обеспечивающих выполнение операторов в требуемой последовательности, тесты должны содержать проверку граничных условий (например, переход по условию х> 10 должен проверяться для значений, больших, меньших и равных 10). Отдельно проверяется реакция программы на ошибочные исходные данные.
Идея нисходящего тестирования предполагает, что к тестированию программы приступают еще до того, как завершено ее проектирование. Это позволяет раньше опробовать основные межмодульные интерфейсы, а также убедиться в том, что программа в основном удовлетворяет требованиям пользователя. Только после того как логическое ядро испытано настолько, что появляется уверенность в правильности реализации основных интерфейсов, приступают к кодированию и тестированию следующего уровня программы.
1. Основные понятия и терминология
Часто некоторую последовательность инструкций требуется повторить в нескольких местах программы. Чтобы программисту не приходилось тратить время и усилия на копирование этих инструкций, в большинстве языков программирования предусматриваются средства для организации подпрограмм (методов в C#). Таким образом, программист получает возможность присвоить последовательности инструкций произвольное имя и использовать это имя в качестве сокращенной записи в тех местах, где встречается соответствующая последовательность инструкций. Такую именованную последовательность инструкций будем называть функцией (методом). Если метод дает одно результирующее значение и, следовательно, может использоваться в выражениях, то такой метод называется функцией. Определение сокращенной записи называется описанием метода или описанием функции. Использование этого сокращения в программе называется оператором или вызовом метода. Функция, если она встречается в выражении, называется вызовом функции или обращением к функции.
Пример: Описание и оператор процедуры.
Для последовательности инструкций
t = r; r = q; q = t (1)
можно ввести сокращение, описав процедуру следующим образом:
void P() (2)
{ t = r; r = q; q = t }
И теперь всякий раз, когда в программе встречается эта последовательность инструкций, ее можно заменить оператором процедуры P. Описание метода состоит из двух частей: заголовка метода и тела метода. Заголовок (первая строка в (2)) содержит идентификатор метода. Тело (вторая строка в (2)) состоит из одной или нескольких инструкций, для которых вводится сокращение.
2. Локальность
Если объект (константа, переменная) имеет смысл только в пределах одного метода, то этот объект называется локальным. В таком случае этой части программы можно присвоить имя (т. е. оформить ее как метод). Локальные объекты метода описываются в его заголовке
Пример: Описание процедуры с описанием локальной переменной
class Example
{
public int r, q;//Поля класса
public void P()(33.4)
{
int t;
t = r;
r = q;
q = t;
}
}
В теле метода используются объекты двух сортов: локальные (в примере — t) и нелокальные объекты. Последние определены в контексте, являющимся средой для описания метода. Если они определены в классе, то такие объекты называются глобальными; если же они определены в самом языке (т. е. в контексте, в который «погружается» программа), то они называются ключевыми словами, например int [3]. Областью существования локального объекта является весь текст метода. Это означает, что после окончания процесса, описанного методом, пространство в памяти, занятое локальными переменными, становится снова свободным и его можно использовать для других переменных. Очевидно, при повторном вызове того же самого метода значения его локальных переменных снова не определены, точно так же как они не были определены при первом вызове метода.
При идентификации локальных объектов существенно то, что мы можем выбирать их имена вне зависимости от среды. Главную программу удобно рассматривать как процедуру без имени.
3. Параметры процедуры
Если некоторая последовательность операций применяется к различным операндам в разных частях программы, то такая последовательность оформляется как функция (метод), а ее операнды становятся параметрами.
Идентификаторы, введенные в заголовке функции для обозначения операндов, называются формальными параметрами.
Они используются только в теле функции и локальны по отношению к ней.
последовательность оформляется как функция (метод), а ее операнды становятся параметрами.Объекты, подставляемые вместо формальных пapaметров при вызове функции, называются фактическими параметрами.
Они задаются в каждом операторе функции или вызове функции. Тип фактического параметра определяется типом формального параметра, который специфицируется в заголовке функции. Помимо спецификации типа параметра необходимо также указать желаемый способ подстановки, поскольку вместо формального параметра можно подставить текущее значение либо имя фактической переменной или выражение. Наиболее часто используют два способа подстановки параметров.
1. Фактический параметр вычисляется, и полученное значение подставляется вместо соответствующего формального параметра. Этот способ называется подстановкой значения и имеет наибольшее распространение.
2. Фактический параметр есть переменная. Определенная таким образом переменная заменяет соответствующий формальный параметр. Этот способ называется подстановкой переменной (ссылки) и используется в тех случаях, когда параметр вычисляется в процедуре и является ее результатом.
Пример: Описание процедуры с параметрами (4)
void P(ref int r, ref int q)//формальные параметры-ссылки r,q;
//они же – локальные переменные процедуры.
{
int t; //локальная переменная
t = r; r = q; q = t
}
void G()
{
int a, b;//глобальные переменные
P(ref a, ref b);//Вызов процедуры с фактическими параметрами a,b.
}
В этом примере приведено описание функции P с двумя формальными параметрами-переменными r, q и локальной переменной t. Эта функция вызывается в разделе операторов функции G и на места формальных параметров подставляются по ссылке (по адресу) глобальные переменные a, b. При выполнении кода процедуры операции будут выполняться над переменными a, b. Поэтому после её завершения результат её работы останется в переменных a, b. Они обменяются значениями.